﻿// ------------------------------------------------------------------------
//    XBMControl - A compact remote controller for XBMC (.NET 3.5)
//    Copyright (C) 2008  Bram van Oploo (bramvano@gmail.com)
//                        Mike Thiels (Mike.Thiels@gmail.com)
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <http://www.gnu.org/licenses/>.
// ------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.IO;
using System.Windows;

namespace XBMC
{
    public class XbmcCommunicator
    {
        public XbmcDatabase Database;
        public XbmcPlaylist Playlist;
        public XbmcControls Controls;
        public XbmcNowPlaying NowPlaying;
        public XbmcStatus Status;
        public XbmcMedia Media;
        public XbmcVideo Video;

        private string _configuredIp;
        private string _xbmcUsername;
        private string _xbmcPassword;
        private const string ApiPath = "/xbmcCmds/xbmcHttp";
        private const string LogFile = "log/Yatse-Remote.log";
        private int _connectionTimeout = 2000;
        public bool DebugEnabled;

        public XbmcCommunicator()
        {
            Database = new XbmcDatabase(this);
            Playlist = new XbmcPlaylist(this);
            Controls = new XbmcControls(this);
            NowPlaying = new XbmcNowPlaying(this);
            Status = new XbmcStatus(this);
            Media = new XbmcMedia(this);
            Video = new XbmcVideo(this);
        }
        
        private static string[] MySplitString(string stringToBeSplitted, string delimeter)
        {
            if (stringToBeSplitted == null)
                throw new ArgumentNullException("stringToBeSplitted");
            if (delimeter == null)
                throw new ArgumentNullException("delimeter");

            var dsum = 0;
            var ssum = 0;
            var dl = delimeter.Length;
            var sl = stringToBeSplitted.Length;

            if (dl == 0 || sl == 0 || sl < dl)
                return new[] { stringToBeSplitted };

            var cd = delimeter.ToCharArray();
            var cs = stringToBeSplitted.ToCharArray();
            var retlist = new List<string>();

            for (var i = 0; i < dl; i++)
            {
                dsum += cd[i];
                ssum += cs[i];
            }

            var start = 0;
            for (var i = start; i < sl - dl; i++)
            {
                if (i >= start && dsum == ssum && stringToBeSplitted.Substring(i, dl) == delimeter)
                {
                    retlist.Add(stringToBeSplitted.Substring(start, i - start));
                    start = i + dl;
                }

                ssum += cs[i + dl] - cs[i];
            }

            if (dsum == ssum && stringToBeSplitted.Substring(sl - dl, dl) == delimeter)
            {
                retlist.Add(stringToBeSplitted.Substring(start, sl - dl - start));
                retlist.Add("");
            }
            else
            {
                retlist.Add(stringToBeSplitted.Substring(start, sl - start));
            }

            return retlist.ToArray();
        }

        public string[][] MultiRequest(string command,string parameter)
        {
            HttpWebRequest request;
            HttpWebResponse response = null;
            StreamReader reader = null;
            SetResponseFormat("openRecord;<record>;closeRecord;</record>;openField;<field>;closeField;</field>");

            command = "?command=" + Uri.EscapeDataString(command);
            command += "(" + Uri.EscapeDataString(parameter) + ")";
            var uri = "http://" + _configuredIp + ApiPath + command;
            string[][] pageContent = null;
            try
            {
                request = (HttpWebRequest)WebRequest.Create(uri);
                request.Method = "GET";
                request.Timeout = _connectionTimeout;
                if (_xbmcUsername != "" && _xbmcPassword != "") request.Credentials = new NetworkCredential(_xbmcUsername, _xbmcPassword);
                response = (HttpWebResponse)request.GetResponse();
                reader = new StreamReader(response.GetResponseStream());//, Encoding.UTF8
                var tempString = reader.ReadToEnd();

                pageContent = MultiRequestToArray(tempString);
                
            }
            catch (WebException e)
            {
                WriteToLog("MULTIREQ - ERROR : " + command + " - " + e.Message);
            }
            finally
            {
                if (response != null) response.Close();
                if (reader != null) reader.Close();
            }

            return pageContent;
        }

        public string[][] MultiMusicRequest(string parameter)
        {
            return MultiRequest("querymusicdatabase", parameter);
        }

        public string[][] MultiVideoRequest(string parameter)
        {
            return MultiRequest("queryvideodatabase", parameter);
        }
        private static string[][] MultiRequestToArray(string requestReturn)
        {
            requestReturn = requestReturn.Replace("<field>", "").Replace("\n", "").Replace("<html>", "").Replace("</html>", "").Replace("<record>", "");

            var temp = requestReturn.Split(new[] { "</record>" }, StringSplitOptions.RemoveEmptyEntries).ToList();
            if (temp.Count<1) return null;

            var returnValue = temp.Select(temp2 => temp2.Split(new[] { "</field>" }, StringSplitOptions.None)).ToList();

            return returnValue.Count < 1 ? null : returnValue.ToArray();
        }

        public bool SetResponseFormat()
        {
            string[] aResult = null;
            var ip = GetIp();

            if (!string.IsNullOrEmpty(ip))
                aResult = Request("SetResponseFormat", null, ip);

            if (aResult == null)
                return false;
            return (aResult[0] == "OK") ? true : false;
        }

        public bool SetResponseFormat(string parameter)
        {
            string[] aResult = null;
            var ip = GetIp();

            if (!string.IsNullOrEmpty(ip))
                aResult = Request("SetResponseFormat", parameter, ip);

            if (aResult == null)
                return false;
            return (aResult[0] == "OK") ? true : false;
        }

        public string Download(string filename)
        {
            HttpWebRequest request;
            HttpWebResponse response = null;
            StreamReader reader = null;
            
            var command = "?command=FileDownload";
            command += "(" + Uri.EscapeDataString(filename) + ")";
            var uri = "http://" + _configuredIp + ApiPath + command;
            string pageContent = null;
            try
            {
                request = (HttpWebRequest)WebRequest.Create(uri);
                request.Method = "GET";
                request.Timeout = 30000;
                if (_xbmcUsername != "" && _xbmcPassword != "") request.Credentials = new NetworkCredential(_xbmcUsername, _xbmcPassword);
                response = (HttpWebResponse)request.GetResponse();
                reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8);

                var tempString = reader.ReadToEnd();
                pageContent = tempString;

            }
            catch (WebException e)
            {
                WriteToLog("DOWNLOAD - ERROR : " + filename + " - " + e.Message);
            }
            finally
            {
                if (response != null) response.Close();
                if (reader != null) reader.Close();
            }

            if (pageContent != null)
                return pageContent.Replace("<html>\n", "").Replace("\n</html>", "").Replace("<html>", "").Replace("</html>", "");
            return pageContent;
        }


        public string[] Request(string command, string parameter, string ip)
        {
            string[] pageItems = null;
            HttpWebRequest request;
            HttpWebResponse response = null;
            StreamReader reader = null;
            string[] pageContent;

            var isQuery = (command.ToLower() == "querymusicdatabase" || command.ToLower() == "queryvideodatabase") ? true : false;

            var ipAddress = ip ?? _configuredIp;
            parameter = parameter ?? "";
            command = "?command=" + Uri.EscapeDataString(command);
            command += (parameter == "") ? "" : "(" + Uri.EscapeDataString(parameter) + ")";
            //command += (parameter == "") ? "" : "&parameter=" + Uri.EscapeDataString(parameter);
            var uri = "http://" + ipAddress + ApiPath + command;

            //WriteToLog(uri);

            try
            {
                request = (HttpWebRequest)WebRequest.Create(uri);
                request.Method = "GET";
                request.Timeout = _connectionTimeout;
                if (_xbmcUsername != "" && _xbmcPassword != "") request.Credentials = new NetworkCredential(_xbmcUsername, _xbmcPassword);
                response = (HttpWebResponse)request.GetResponse();
                reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8);

                string tempString;
                if (isQuery)
                {
                    tempString = reader.ReadToEnd().Replace("</field>", "").Replace("\n", "").Replace("<html>", "").Replace("</html>", "").Replace("<record>", "").Replace("</record>", "");
                    pageContent = MySplitString(tempString, "<field>");
                }
                else
                {
                    tempString = reader.ReadToEnd().Replace("\n", "").Replace("<html>", "").Replace("</html>", "");
                    pageContent = MySplitString(tempString, "<li>");
                }

                if (pageContent != null)
                {
                    if (pageContent.Length > 1)
                    {
                        pageItems = new string[pageContent.Length-1];

                        for (var x = 1; x < pageContent.Length; x++)
                            pageItems[x-1] = pageContent[x];
                    }
                    else
                    {
                        pageItems = new string[1];
                        pageItems[0] = pageContent[0];
                    }
                }
            }
            catch (WebException e)
            {
                WriteToLog("REQUEST - ERROR : " + command + " - " + e.Message);
            }
            finally
            {
                if (response != null) response.Close();
                if (reader != null) reader.Close();
            }

            return pageItems;
        }

        public string[] Request(string command, string parameter)
        {
            return Request(command, parameter, null);
        }

        public string[] Request(string command)
        {
            return Request(command, null, null);
        }

        private void WriteToLog(string message)
        {
            if (!DebugEnabled) return;
            try
            {
                var sw = new StreamWriter(LogFile, true);
                sw.WriteLine(DateTime.Now + " : " + message);
                sw.Flush();
                sw.Close();
            }
            catch (Exception)
            {
            }
        }

        public void SetIp(string ip)
        {
            _configuredIp = ip;
            if (string.IsNullOrEmpty(_configuredIp))
                Status.DisableHeartBeat();
            else
                Status.EnableHeartBeat();
        }

        public string GetIp()
        {
            return _configuredIp;
        }

        public void SetCredentials(string username, string password)
        {
            _xbmcUsername = username;
            _xbmcPassword = password;
        }

        public void SetConnectionTimeout(int timeOut)
        {
            _connectionTimeout = timeOut;
        }
    }
}
